Official ULAplus(TM) Specification

The ULAplus(TM) specification describes an enhanced ULA for the Sinclair ZX
Spectrum family of computers. It can be implemented as a plug-in replacement for
the ULA such as SLAM+, an external video adapter such as ZXHD, in emulators, or
in modern clones such as the ZX-Uno. It is designed for maximum compatibility
with existing software. This information supersedes all existing versions of the
specification, including those with later version numbers.

The latest official version of the specification is version 1.1d. 

Revision History

Version 1.1d
Restored missing information about palettes in Timex hi-res mode.

Version 1.1c
Revised to clarify that unused modes and groups are reserved for future use.

Version 1.1b
Revised to clarify the values returned when reading from the ports. Added quick
palette set example.

Version 1.1a
Revised for the release of Fuse 1.2, incorporating changes from Mark Smith's
ULAplus(TM) implementation. This version deprecates grayscale mode and adds the
Timex screen mode control register to the SZX palette block.

Version 1.1
Revised for the release of ZXDS 1.3, incorporating changes from the OpenCores
ULAplus(TM) implementation. This version rationalizes the specification.
Software written for the standard mode of the new specification will run
unmodified on earlier implementations.

   * Includes the Timex video modes (optional).
   * Changes the preferred way of multiplexing the 2-bit blue value to obtain a
     3-bit blue value.
   * Adds 256 grayscale support (optional).
   * Deprecated HSL and CMYK support.

Version 1.0
First published version of the specification.

I/O PORTS
ULAplus(TM) is controlled by two ports. Port BF3B is the register port (write
only). The byte output is interpreted as follows:

   * Bits 0-5: Select the register sub-group
   * Bits 6-7: Select the register group.

Two groups are available:

   * 00 - Palette Group
     When this group is selected, the sub-group determines the entry in the
	 palette table (0-63).

   * 01 - Mode Group
     The sub-group is (optionally) used to mirror the video functionality of
	 Timex port FF and the framebuffer select of port 7FFD as follows:

        * bits 0-2: Screen Mode
          000: screen 0, 001: screen 1, 010: hi-color, 110: hi-res (bank 5)
          100: screen 0, 101: screen 1, 011: hi-color, 111: hi-res (bank 7)

        * bits 3-5: Screen Color in Hi-Res Mode (foreground/background)
          000: 0/7, 001: 1/6, 010: 2/5, 011: 3/4
          100: 4/3, 101: 5/2,  110: 6/1, 111: 7/0

Port FF3B is the data port (read/write). When the palette group is selected, the
byte written will describe the color. When the mode group is selected, the byte
output will be interpreted as follows:

          bit 0: ULAplus(TM) palette on (1) or off (0)

Reading from port FF3B returns the last data byte written to the currently
selected register. This can be used to read back the current palette or
determine if palette mode is active.

Implementations that support the Timex video modes use port FF as the primary
means to set the video mode, as per the Timex machines. It is left to the
individual implementations to determine if reading the port returns the previous
write or the floating bus.

Note: Modes 2 to 255 are reserved for future use. Groups 10 and 11 are reserved
for future use.

GRB PALETTE ENTRIES
For a device using the GRB color space, the palette entry is interpreted as
follows:

   * bits 0-1: Blue Intensity
   * bits 2-4: Red Intensity
   * bits 5-7: Green Intensity

This color space uses a sub-set of 9-bit GRB. The missing lowest blue bit is set
to OR of the other two blue bits (Bb becomes 000 for 00, and Bb1 for anything
else). This gives access to a fixed half the potential 512 color palette. This
reduces the jump in intensity in the lower range in the earlier version of the
specification. It also means the standard palette can now be represented by the
ULAplus(TM) palette.

GRAYSCALE PALETTE ENTRIES (DEPRECATED)
In grayscale mode, each palette entry describes an intensity from zero to 255.
This can be achieved by simply removing the colour from the output signal.

LIMITATIONS
Although in theory 64 colors can be displayed at once, in practice this is
usually not possible except when displaying color bars, because the four CLUTs
are mutually exclusive; it is not possible to mix colors from two CLUTs in the
same cell. However, with software palette cycling it is possible to display all
256 colors on screen at once.

EMULATION
The 64 color mode look-up table is organized as four palettes of 16 colors.

Bits 7 and 6 of each attribute byte, normally used for FLASH and BRIGHT, are
used as an index value (0-3) to select one of the four color palettes.

Each color palette has 16 entries: eight for INK, and eight for PAPER. Bits 0-2
(INK) and 3-5 (PAPER) of the attribute byte are used as indexes to retrieve
color data from the selected palette.

With the standard Spectrum display, the BORDER color is the same as the PAPER
color in the first CLUT. For example BORDER 0 would set the border to the same
color as PAPER 0 (with the BRIGHT and FLASH bits not set).

The complete index can be calculated as:

   * ink color = (FLASH * 2 + BRIGHT) * 16 + INK
   * paper color = (FLASH * 2 + BRIGHT) * 16 + PAPER + 8

When scaling 3-bits of color data to more bits for emulators that operate in
high color mode, simply concatenate the bits repeatedly and then truncate to as
many bits as needed. For example, for 8-bits the following conversion should be
used:

   * 76543210
   * hmlhmlhm

where h is the high bit, m is the middle bit, and l is the low bit of the
original 3-bit value.

With the Timex hi res display, the BORDER color is the same as the PAPER color
in the second CLUT. Bits 3-5 of port FF set the INK, PAPER, and BORDER values to
the following ULAplus(TM) palette registers:

   BITS   INK   PAPER   BORDER
   000     24     31     31
   001     25     30     30
   010     26     29     29
   011     27     28     28
   100     28     27     27
   101     29     26     26
   110     30     25     25
   111     31     24     24

EXTENSION TO THE ZX-STATE (SZX) FORMAT

ZXSTPALETTE
The state of the ULA registers found in the 64 color replacement ULA. This block
may be present for any machine. 

// Palette Block flags
#define ZXSTPALETTE_DISABLED 0
#define ZXSTPALETTE_ENABLED 1

// Palette Block (contains the palette register values)
typedef struct _tagZXSTPALETTEBLOCK
{
  ZXSTBLOCK blk;
  BYTE chFlags;
  BYTE chCurrentRegister;
  BYTE chPaletteRegs[64];
  BYTE chFf;
} ZXSTPALETTEBLOCK, *LPZXSTPALETTEBLOCK;

Members

blk
The block header. The block id is ZXSTBID_PALETTE ('P', 'L', 'T', 'T').

chFlags
A flags that indicates if the palette is enabled or if the normal display mode
is in use. This can be one of:

   * ZXSTPALETTE_DISABLED / Normal palette mode with BRIGHT and FLASH
   * ZXSTPALETTE_ENABLED / 64 color palette mode

chCurrentRegister
The currently selected palette register (0-63).

chPaletteRegs
The current values of the palette registers.

chFf
The current value of port FF which controls the Timex screen mode, and high
resolution colors. Added in v1.1a of the specification.

EXTENSION TO THE SCR FORMAT

A 6912 byte .SCR file contains a standard Spectrum screen.

A 6976 byte .SCR file contains a standard Spectrum screen followed by 64 color
registers.

A 12288 byte .SCR file contains a Timex hi-color screen.

A 12352 byte .SCR file contains a Timex hi-color screen followed by 64 color
registers.

A 12289 byte .SCR file contains a Timex hi-res screen.

A 12353 byte .SCR file contains a Timex hi-res screen followed by the hi-res
color information that was dumped from port 255, followed by 64 color registers.

PALETTE FILE FORMAT

The palette format doubles as the BASIC patch loader. This enables you to edit
patches produced by other people.

; 64 color palette file format (internal) - version 1.0
; Copyright (c) 2009 Chloe Corporation
;
; The palette file is stored as a BASIC program with embedded machine code

header:
    defb 0x00;                           program file
    defb 0x14, 0x01, "64colour";         file name
    defw 0x0097;                         data length
    defw 0x0000;                         autostart line
    defw 0x0097;                         program length

; 0 RANDOMIZE USR ((PEEK VAL "2
; 3635"+VAL "256"*PEEK VAL "23636"
; )+VAL "48"): LOAD "": REM
basic:
    defb 0x00, 0x00, 0x93, 0x00, 0xf9, 0xc0, 0x28, 0x28
    defb 0xbe, 0xb0, 0x22, 0x32, 0x33, 0x36, 0x33, 0x35
    defb 0x22, 0x2b, 0xb0, 0x22, 0x32, 0x35, 0x36, 0x22
    defb 0x2a, 0xbe, 0xb0, 0x22, 0x32, 0x33, 0x36, 0x33
    defb 0x36, 0x22, 0x29, 0x2b, 0xb0, 0x22, 0x34, 0x38
    defb 0x22, 0x29, 0x3a, 0xef, 0x22, 0x22, 0x3a, 0xea

start:
    di;             disable interrupts
    ld hl, 38;      HL = length of code
    add hl, bc;     BC = entry point (start) from BASIC
    ld bc, 0xbf3b;  register select
    ld a, 64;       mode group
    out (c), a;
    ld a, 1;
    ld b, 0xff;     choose register port
    out (c), a;     turn palette mode on
    xor a;          first register

setreg:
    ld b, 0xbf;     choose register port
    out (c), a;     select register
    ex af, af';     save current register select
    ld a, (hl);     get data
    ld b, 0xff;     choose data port
    out (c), a;     set it
    ex af, af';     restore current register
    inc hl;         advance pointer
    inc a;          increase register
    cp 64;          are we nearly there yet?
    jr nz, setreg;  repeat until all 64 have been done
    ei;             enable interrupts
    ret;            return

; this is where the actual data is stored.; The following is an example palette.

registers:
    defb 0x00, 0x02, 0x18, 0x1b, 0xc0, 0xc3, 0xd8, 0xdb; INK
    defb 0x00, 0x02, 0x18, 0x1b, 0xc0, 0xc3, 0xd8, 0xdb; PAPER
    defb 0x00, 0x03, 0x1c, 0x1f, 0xe0, 0xe3, 0xfc, 0xff; +BRIGHT
    defb 0x00, 0x03, 0x1c, 0x1f, 0xe0, 0xe3, 0xfc, 0xff;
    defb 0xdb, 0xd8, 0xc3, 0xc0, 0x1b, 0x18, 0x02, 0x00; +FLASH
    defb 0xdb, 0xd8, 0xc3, 0xc0, 0x1b, 0x18, 0x02, 0x00;
    defb 0xff, 0xfc, 0xe3, 0xe0, 0x1f, 0x1c, 0x03, 0x00; +BRIGHT/
    defb 0xff, 0xfc, 0xe3, 0xe0, 0x1f, 0x1c, 0x03, 0x00; +FLASH

terminating_byte:
    defb 0x0d;

QUICK PALETTE SET

set_palette:
    ld c, 0x3b;           ULAplus(TM) port
    ld de, 0x00bf;        d = data, e = register
    ld hl, pal_end;       mode group register
    ld a, 65;             becomes 64

palette_loop:
    dec a;                next register
    ld b, e;              register port
    out (c), a;           select register
    ld b, d;              data port
    outd;                 out bc, (hl); dec hl; dec b
    and a;                was that the last register?
    jr nz, palette_loop;  set all 64 entries
    ret;                  done

palette:
    incbin "palette.bin"; 64 bytes of G3R3B2 palette register values
pal_end:
    defb 1;               write 1 to register 64 to enable ULAplus mode

LEGAL

ULAplus(TM) is a trademark of Chloe Corporation. ULAplus(TM) is a royalty-free
open format. The official ULAplus(TM) specification is released under the
Creative Commons Attribution-Share Alike License.